package net.omnivention.wulumuqi;

import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.Enumeration;
import java.util.List;
import java.util.regex.Pattern;

import javax.servlet.ServletContext;
import javax.servlet.ServletInputStream;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

import net.omnivention.wulumuqi.html.HtmlElementFactory;

import org.apache.bsf.BSFException;
import org.apache.bsf.BSFManager;
import org.apache.log4j.Logger;
import org.jdom.Document;
import org.jdom.Element;
import org.jdom.JDOMException;
import org.jdom.Text;
import org.jdom.xpath.XPath;


public class HtmlDocument extends Document {
	private final static Logger logger = Logger.getLogger(HtmlDocument.class);

	private BSFManager manager;

	private WuluClassLoader urlClassLoader;

	private String contextURL;

	private ServletContext servletContext;

	private HttpServletRequest request;

	public HtmlDocument(Element rootElement) {
		super(rootElement);
	}

	public static Element createElement(String elementName) {
		return HtmlElementFactory.createElement(elementName);
	}

	void init() {
		URL[] urls = new URL[1];
		try {
			String baseURL = getBaseURI();
			contextURL = WuluUtil.getContextURL(baseURL);
			urls[0] = new URL(contextURL);
			urlClassLoader = new WuluClassLoader(urls, HtmlDocument.class.getClassLoader());
		} catch (MalformedURLException e) {
			logger.error(e);
		}

		manager = new BSFManager();
		try {
			manager.loadScriptingEngine("jython");
			manager.loadScriptingEngine("ruby");
			manager.declareBean("document", this, HtmlDocument.class);
			manager.setClassLoader(urlClassLoader);
		} catch (BSFException e) {
			logger.error(e);
		}

	}

	public Element getElementById(String id) throws JDOMException {
		String xpathQuery = "//*[@id='" + id + "']";
		return selectNode(xpathQuery);
	}

	public List selectNodes(String xpathExpression) {
		try {
			XPath xpath = WuluUtil.createXPath(xpathExpression);
			return xpath.selectNodes(this);
		} catch (JDOMException e) {
			logger.warn(e, e);
		}
		return null;
	}

	public Element selectNode(String xpathExpression) {
		List nodes = selectNodes(xpathExpression);
		if (nodes.size() > 0) {
			return (Element) nodes.get(0);
		}
		return null;
	}

	void applyManipulatorsInMetaTag() throws IOException {
		Element meta = selectNode("//html:meta[@docManipulators]");
		if (meta != null) {
			String docManipulators = meta.getAttributeValue("docManipulators");
			applyManipulatorsInQueryString(docManipulators);
		}
	}

	void applyManipulatorInWuluScriptTag() {
		List wuluScripts = selectNodes("//html:wuluScript");
		try {
			for (int i = 0; i < wuluScripts.size(); i++) {
				Element aScriptElement = (Element) wuluScripts.get(i);
				String script = aScriptElement.getText();
				logger.debug(script);
				manager.exec("jython", script, 0, 0, script);
			}
		} catch (BSFException e) {
			logger.error(e, e);
		}
	}

	void removeManipulatorsInMetaTag() {
		List metas = selectNodes("//html:meta[@docManipulators]");
		for (int i = 0; i < metas.size(); i++) {
			Element m = (Element) metas.get(i);
			m.detach();
		}
	}

	public void applyManipulator(String manipulatorURL) throws IOException {
		try {
			logger.info("manipulatorURL=" + manipulatorURL);
			String script = WuluUtil.getResource(manipulatorURL);
			if (manipulatorURL.indexOf(".py") > -1) {
				manager.exec("jython", manipulatorURL, 0, 0, script);
			} else if (manipulatorURL.indexOf(".rb") > -1) {
				manager.exec("ruby", manipulatorURL, 0, 0, script);
			}
		} catch (BSFException e) {
			logger.error(e, e);
		}
	}

	/**
	 * comma delimited string of manipulators
	 * 
	 * @param docManipulatorURLs
	 * @throws IOException
	 */
	public void applyManipulatorsInQueryString(String docManipulatorURLs) throws IOException {
		String[] manipulatorURLs = parseManipulatorURLs(docManipulatorURLs);
		for (int i = 0; i < manipulatorURLs.length; i++) {
			String manURL = manipulatorURLs[i];
			applyManipulator(manURL);
		}
	}

	private String[] parseManipulatorURLs(String docManipulatorsParam) {

		String[] urls = docManipulatorsParam.split(",");
		for (int i = 0; i < urls.length; i++) {
			String url = urls[i];
			if (url.indexOf("http://") == -1) {
				urls[i] = "file:///" + servletContext.getRealPath(url);
			}
		}

		return urls;
	}

	public HttpServletRequest getRequest(){
		return this.request;
	}
	
	void setRequest(HttpServletRequest request) {
		try {
			this.request = request;
			manager.declareBean("request", request, HttpServletRequest.class);
			manager.declareBean("input", request.getInputStream(), ServletInputStream.class);
			manager.declareBean("session", request.getSession(), HttpSession.class);
		} catch (BSFException e) {
			logger.error(e);
		} catch (IOException e) {
			logger.error(e);
		}
	}

	void setResponse(HttpServletResponse response) {
		try {
			manager.declareBean("response", response, HttpServletResponse.class);
			manager.declareBean("out", response.getOutputStream(), ServletOutputStream.class);
		} catch (BSFException e) {
			logger.error(e);
		} catch (IOException e) {
			logger.error(e);
		}
	}

	void setServletContext(ServletContext servletContext) {
		try {
			this.servletContext = servletContext;
			manager.declareBean("servletContext", servletContext, ServletContext.class);
		} catch (BSFException e) {
			logger.error(e);
		}
	}

	@SuppressWarnings("unchecked")
	public boolean validateForm() {
		boolean formValid = true;

		Enumeration names = request.getParameterNames();

		while (names.hasMoreElements()) {
			String name = (String) names.nextElement();
			if (name.equals("scriptURL") || name.equals("htmlFile")) {
				continue;
			}

			String xpath = "//html:form/descendant::html:input[@name='" + name + "']";
			Element input = selectNode(xpath);
			if (input != null) {
				String value = request.getParameter(name);
				input.setAttribute("value", value);
				String regex = input.getAttributeValue("regex");
				if (regex != null) {
					boolean inputValid = Pattern.matches(regex, value);
					logger.debug(inputValid + " " + regex + " " + value);
					if (!inputValid) {
						Element parent = input.getParentElement();
						int i = parent.indexOf(input);
						List content = parent.getContent();
						String msg = input.getAttributeValue("notMatchMsg");

						content.add(i, new Text(msg));
						formValid = false;
					}
				}
			}
		}

		if (formValid == false) {
			Element input = createElement("input");
			input.setAttribute("type", "hidden");
			input.setAttribute("name", "htmlFile");
			String htmlFile = (String) request.getAttribute("htmlFile");
			input.setAttribute("value", htmlFile);
			Element form = selectNode("//html:form");
			form.addContent(input);
			logger.debug(htmlFile);
		}

		return formValid;
	}

}
